package com.xukun.changgou.auth.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xukun.changgou.auth.feign.sys.SysUserFeign;
import com.xukun.changgou.auth.model.BaseUserDetails;
import com.xukun.changgou.common.constants.CommonConstant;
import com.xukun.changgou.common.redis.RedisUtil;
import com.xukun.changgou.common.response.ResponseData;
import com.xukun.changgou.common.response.ResponseEnum;
import com.xukun.changgou.common.response.exception.BaseException;
import com.xukun.changgou.common.utils.HttpUtil;
import com.xukun.changgou.common.utils.JwtUtil;
import com.xukun.changgou.service.api.sys.enums.UserOnlineEnum;
import com.xukun.changgou.service.api.sys.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author xukun
 * @Date 2021-06-03 16:36
 * @Description 登录过滤器
 */
public class BaseLoginFilter extends UsernamePasswordAuthenticationFilter {

    @Autowired
    private SysUserFeign userFeign;

    @Autowired
    private RedisUtil redisUtil;

    public BaseLoginFilter() {
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/auth/login", "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //如果是请求类型是json字符串
        if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = null;
            ObjectMapper objectMapper = new ObjectMapper();
            try (ServletInputStream inputStream = request.getInputStream()) {
                //转化为BaseUserDetails
                BaseUserDetails user = objectMapper.readValue(inputStream, BaseUserDetails.class);
                usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
            } catch (IOException e) {
                e.printStackTrace();
                usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken("", "");
            } finally {
                //这一步将会调用UserDetailsService中的loadUserByUsername方法进行
                return this.getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken);
            }
        } else {
            return super.attemptAuthentication(request, response);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        String username = authResult.getName();
        ResponseData userData = userFeign.queryUserByCode(username);
        UserModel userModel = JSON.parseObject(JSON.toJSONString(userData.getData()), UserModel.class);
        userFeign.update(new UserModel().setId(userModel.getId()).setOnlineFlag(UserOnlineEnum.getValue(UserOnlineEnum.在线)).setLastLoginTime(System.currentTimeMillis()));

        //权限数据放入redis缓存中
        Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
        List<String> authList = new ArrayList<>();
        authorities.forEach(auth -> authList.add(auth.getAuthority()));
        redisUtil.set(username + CommonConstant.REDIS_USER_AUTH_SUFFIX, JSON.toJSONString(authList));
        //设置过期时间，和token保持一致
        redisUtil.expire(username + CommonConstant.REDIS_USER_AUTH_SUFFIX, CommonConstant.TOKEN_TTL, TimeUnit.MILLISECONDS);

        //生成token
        String subject = userModel.getId() + "|" + userModel.getCode();
        String token = JwtUtil.generateToken(subject);

        //登录成功返回token
        HttpUtil.doReturn(response, true, ResponseEnum.SUCCESS, token);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if (exception.getCause() instanceof BaseException) {
            BaseException baseException = (BaseException) exception.getCause();
            //打印
            baseException.printStackTrace();
            HttpUtil.doReturn(response, baseException);
        }

        String message = "";
        //打印错误信息
        exception.printStackTrace();
        if (exception instanceof UsernameNotFoundException) {
            message = "用户不存在";
        } else if (exception instanceof DisabledException) {
            message = "账号被禁用";
        } else if (exception instanceof BadCredentialsException) {
            message = "密码错误";
        } else {
            message = exception.getMessage();
        }
        HttpUtil.doReturn(response, false, message);
    }
}